<template>
  <div class="top-area">
    <div class="city-choose">{{ currentCity }}</div>
    <div class="search-wrapper">
      <i class="iconfont icon-sousuo"></i>
      <el-input v-model.trim="input" style="width: 240px" placeholder="请搜索你所在的城市" @change="handleCitySearch" />
    </div>
    <el-select v-model.trim="value1" placeholder="选择绘制图形" style="width: 240px" @change="handleDrawingToolChange">
      <el-option v-for="item in options1" :key="item.value" :value="item.value" />
    </el-select>
    <div class="select-wrapper" @click="handleTagSelectClick">
      <el-select v-model.trim="value2" placeholder="选择标记类型" style="width: 240px" @change="handleAddTag"
        :disabled="isTagSelectDisabled">
        <el-option v-for="item in options2" :key="item.value" :value="item.value" />
      </el-select>
    </div>
  </div>
  <!-- 地图 -->
  <div id="map-container"></div>
  <div id="popup"></div>
  <div id="overlay"></div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { Map, View, Feature } from "ol";
import TileLayer from "ol/layer/Tile";
import XYZ from "ol/source/XYZ.js";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector.js";
import { defaults as defaultControls } from "ol/control"
import GeoJSON from "ol/format/GeoJSON.js"  //加载数据格式
import Draw from "ol/interaction/Draw.js"   //获取绘制相关功能
import Style from "ol/style/Style.js"  //加载样式
import Overlay from "ol/Overlay.js"  //添加覆盖物
import { Point } from "ol/geom";
import Icon from "ol/style/Icon.js";    //加载图标
import Text from "ol/style/Text.js"  //加载文字
import { getLength } from "ol/sphere";
import Fill from "ol/style/Fill.js"
import Stroke from "ol/style/Stroke.js";
import { getIpData, getCityData } from './api';
import AnimatedClusterLayer from "ol-ext/layer/AnimatedCluster.js";
import Cluster from "ol/source/Cluster.js";
import CircleStyle from "ol/style/Circle";
import { ElMessage } from 'element-plus'
import * as turf from "@turf/turf";
import { debounce } from 'lodash';
const isTagSelectDisabled = ref(true);
const input = ref('')
const value1 = ref('')
const value2 = ref('')
const options1 = [
  {
    value: '直线',
  },
  {
    value: '圆形',
  },
  {
    value: '多边形',
  },
  {
    value: '自由画笔',
  },
  {
    value: '清除绘图',
  },
]

const options2 = [
  { value: '充电桩' },
  { value: '公交站点' },
  { value: '停车场' },
  { value: '退出标记' }
];
let currentCity = ref('')
const map = ref(null)
const cityLayer = ref(null)
const isCityExist = ref(null)
const viewingCity = ref(null)
const adcode = ref(null)
const url = ref(null)
const activeDrawInteraction = ref(false)
const drawLayers = ref([]);
const isAdd = ref(false);
let areaOverlay = null;
let mapClickHandler = null;
let clusterSource = null;
let clusterLayer = null;
let vectorSource = null;
//初始化加载地图
const Initialize = async () => {
  //获取当地ip
  const res = await getIpData()
  currentCity.value = res.city
  viewingCity.value = currentCity.value
  //加载地图
  const view = new View({
    projection: "EPSG:4326",
    center: [114.31, 30.59],
    zoom: 4,
  })
  const Tilelayer = new TileLayer({
    source: new XYZ({
      url: "http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}",
    })
  })
  map.value = new Map({
    target: "map-container",
    view,
    layers: [Tilelayer],
    controls: defaultControls({
      rotate: false,
    })
  })
}
//移动至指定地区
const ViewCity = async () => {
  //根据adcode获取对应的城市信息
  const res2 = await getCityData(viewingCity.value)
  adcode.value = res2.geocodes[0].adcode
  if (cityLayer.value) {
    map.value.removeLayer(cityLayer.value)
  }
  if (adcode.value) {
    url.value = `https://geo.datav.aliyun.com/areas_v3/bound/${adcode.value}.json`
    const citySource = new VectorSource({
      url: url.value,
      format: new GeoJSON(),
    })
    citySource.on("featuresloadend", () => {
      let features = citySource.getFeatures()
      let center = features[0].get("center")
      map.value.getView().animate({
        center: center,
        zoom: 9,
        duration: 2000,
      })
    })
    cityLayer.value = new VectorLayer({
      source: citySource,
      style: setCityStyle
    })
    map.value.addLayer(cityLayer.value)
  }
}
//设置样式
const setCityStyle = (f) => {
  if (!viewingCity.value) return
  let name = f.get("name");
  const view = map.value.getView()
  const zoom = view.getZoom()
  if (name === viewingCity.value && zoom < 10) {
    return new Style({
      fill: new Fill({
        color: "rgba(255,0,0,0.5)",
      }),
      stroke: new Stroke({
        color: "black",
        width: 2,
      })
    })
  } else {
    return new Style();
  }
}
//禁用点击时出弹框
const handleTagSelectClick = () => {
  if (isTagSelectDisabled.value) {
    ElMessage.error('请放大到13级以上')
  }
}
//输入飞行至搜索的城市
const handleCitySearch = debounce(async () => {
  if (!input.value) return
  if (input.value === viewingCity.value) return
  //找到具体的城市或者省份
  try {
    const res = await getCityData(input.value)
    if (res?.geocodes?.length > 0) {
      isCityExist.value = true
      //匹配
      const { level, province, city, district } = res.geocodes[0]
      switch (level) {
        case '省':
          viewingCity.value = province
          break
        case '市':
          viewingCity.value = city
          break
        case '区县':
          viewingCity.value = district
          break
        default:
          viewingCity.value = district
      }
      ViewCity()
    } else {
      isCityExist.value = false
      viewingCity.value = ''
      alert('未找到该城市')
      return
    }
  } catch (error) {
    isCityExist.value = null
    viewingCity.value = ''
  }

},300)
//点击添加对应的画笔
const handleDrawingToolChange = () => {
  if (value1.value === options1[0].value) {
    DrawLine()
  } else if (value1.value === options1[1].value) {
    DrawCircle()
  } else if (value1.value === options1[2].value) {
    DrawPolygon()
  } else if (value1.value === options1[3].value) {
    DrawFreehandDrawer()
  } else {
    clearDrawings()
  }
}
//移除当前画笔
const removeActiveDrawInteraction = () => {
  if (activeDrawInteraction.value && map.value) {
    map.value.removeInteraction(activeDrawInteraction.value);
    activeDrawInteraction.value = null;
  }
};
//默认样式
const defaultDrawingStyle = new Style({
  stroke: new Stroke({
    color: "black",
    width: "2",
  }),
  fill: new Fill({
    color: "rgba(255,0,0,0.5)",
  })
});
//添加图层存放绘图
const createVectorLayer = () => {
  const vecLayer = new VectorLayer({
    source: new VectorSource(),
  });
  map.value.addLayer(vecLayer);
  drawLayers.value.push(vecLayer);
  return vecLayer;
};
//绘图交互工具
const createDrawInteraction = (type, source, freehand = false) => {
  const draw = new Draw({
    type: type,
    source: source,
    freehand: freehand,
  });
  
  draw.on("drawend", (e) => {
    const finalFeature = e.feature;
    finalFeature.setStyle(defaultDrawingStyle);
  });
  
  activeDrawInteraction.value = draw;
  map.value.addInteraction(draw);
  return draw;
};
const ensureAreaOverlay = () => {
  if (!areaOverlay) {
    areaOverlay = new Overlay({
      element: document.createElement('div'),
      positioning: 'bottom-center',
      stopEvent: false
    });
    map.value.addOverlay(areaOverlay);
  }
  return areaOverlay;
};
//显示面积
const showAreaInfo = (area, center) => {
  if (areaOverlay) {
    const areaElement = areaOverlay.getElement();
    areaElement.innerHTML = `<div style="background: white; padding: 4px 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 12px;">
      面积: ${area.toFixed(2)}平方米
    </div>`;
    areaOverlay.setPosition(center);
  }
};
//画线
 const DrawLine = () => {
  removeActiveDrawInteraction()
  const vecLayer = createVectorLayer()
  const draw = createDrawInteraction("LineString", vecLayer.getSource())
  
  const overlay = new Overlay({
    element: document.querySelector("#popup"),
    position: undefined,
    offset: [10, 10]
  })
  map.value.addOverlay(overlay)

  //结束绘制时触发
  draw.on("drawend", function (e) {
    //获取刚才绘制的要素的几何体
    let f = e.feature;
    let g = f.getGeometry();
    const length = getLength(g, { projection: "EPSG:4326" }) / 1000;

    let coordinate = g.getCoordinates();
    let location = coordinate[coordinate.length - 1];
    //添加文本
    const point = new Feature({
      geometry: new Point(location),
    })
    point.setStyle(
      new Style({
        text: new Text({
          text: `最终路径为:${length.toFixed(2)}km`,
          font: "15px sans-serif",
          offsetY: 10,
          stroke: new Stroke({
            color: "red",
          }),
          fill: new Fill({
            color: "red",
          })
        })
      })
    )
    let source = vecLayer.getSource();
    source.addFeature(point)
    overlay.setPosition(undefined)

  })

  //即时测距
  draw.on("drawstart", function (e) {
    const f = e.feature;  //要素
    const g = f.getGeometry()  //几何体
    g.on("change", function (e) {
      let g = e.target;  //几何体
      let coordinate = g.getCoordinates();
      let location = coordinate[coordinate.length - 1];
      overlay.setPosition(location)
      let length = getLength(g, { projection: "EPSG:4326" });
      length = length / 1000;
      overlay.getElement().innerHTML = "当前绘制的线的长度为" + length.toFixed(2) + "km"
    })
  })
}
//画圆
 const DrawCircle = () => {
  removeActiveDrawInteraction()
  const vecLayer = createVectorLayer()
  ensureAreaOverlay()
  const draw = createDrawInteraction("Circle", vecLayer.getSource(), false)
  
  draw.on("drawend", (e) => {
    //计算面积
    const geometry = e.feature.getGeometry();
    const center = geometry.getCenter(); 
    const radius = geometry.getRadius();
    const centerPoint = turf.point(center);
    const turfCircle = turf.circle(centerPoint, radius, {
      units: 'meters', 
      steps: 64 
    });
    const area = turf.area(turfCircle);
    showAreaInfo(area, center)
  });
}
//画多边形
 const DrawPolygon = () => {
  removeActiveDrawInteraction()
  const vecLayer = createVectorLayer()
  ensureAreaOverlay()
  const draw = createDrawInteraction("Polygon", vecLayer.getSource(), false)
  
  draw.on("drawend", (e) => {
    //计算面积
    const geometry = e.feature.getGeometry();
    const coordinates = geometry.getCoordinates();
    const polygon = turf.polygon(coordinates);
    const area = turf.area(polygon);
    const center = geometry.getInteriorPoint().getCoordinates();
    showAreaInfo(area, center)
  });
}
//自由画笔
 const DrawFreehandDrawer = () => {
  removeActiveDrawInteraction()
  const vecLayer = createVectorLayer()
  const draw = createDrawInteraction("LineString", vecLayer.getSource(), true)
  
  draw.on("drawend", (e) => {
    const finalFeature = e.feature;
    finalFeature.setStyle(
      new Style({
        stroke: new Stroke({
          color: "black",
          width: "2",
        })
      })
    );
  });
}
//清除绘图
 const clearDrawings = () => {
  removeActiveDrawInteraction();
  if (map.value && drawLayers.value.length > 0) {
    drawLayers.value.forEach(layer => {
      map.value.removeLayer(layer)
    })
    drawLayers.value = [];
  }
  // 清除面积覆盖物
  if (areaOverlay) {
    areaOverlay.setPosition(undefined);
  }
}
//添加标记
const handleAddTag = () => {
  const selectionOption = options2.find(item => item.value === value2.value)
  if (selectionOption) {
    if (selectionOption.value === "退出标记") {
      isAdd.value = false;
      if (mapClickHandler && map.value) {
        map.value.un("click", mapClickHandler);
        mapClickHandler = null;
      }
    } else {
      isAdd.value = true;
      HandleAddClusterLayer(selectionOption.value)
    }
  }
}
//聚合动画
const HandleAddClusterLayer = (icon) => {
  if (mapClickHandler && map.value) {
    map.value.un('click', mapClickHandler)
  }
  if (!clusterSource) {
    vectorSource = new VectorSource()
    clusterSource = new Cluster({
      source: vectorSource,
      distance: 40,
    })
    clusterLayer = new AnimatedClusterLayer({
      source: clusterSource,
      animationDuration: 300,
      style: function (feature) {
        const size = feature.get('features').length
        //size表示是否集合
        if (size > 1) {
          return new Style({
            image: new CircleStyle({
              radius: 10 + size,
              fill: new Fill({
                color: 'rgb(51,153,204)',
              }),
            }),
            text: new Text({
              text: size.toString(),
              font: '16px Arial',
              fill: new Fill({
                color: 'white',
              }),
            }),
          })
        } else {
          const singleFeature = feature.get('features')[0]
          const iconType = singleFeature.get('iconType')
          return new Style({
            image: new Icon({
              src: `src/image/${iconType}.png`,
              anchor: [0.5, 0.95],
              scale: 0.6,
            }),
          })
        }
      },
    })
  }
  map.value.addLayer(clusterLayer)

  mapClickHandler = function (e) {
    const coordinate = e.coordinate
    const point = new Feature({
      geometry: new Point(coordinate),
      iconType: icon,
    })
    vectorSource.addFeature(point)
  }
  map.value.on('click', mapClickHandler)
}
//添加overlay
const Addoverlay = () => {
  const overlay = new Overlay({
    element: document.querySelector("#overlay"),    //将div添加到overlay中
    position: undefined,
    positioning: "bottom-center",  //定位点  
  })
  map.value.addOverlay(overlay);
  map.value.on("click", function (e) {
    const coordinate = e.coordinate;
    const source = cityLayer.value.getSource()
    let features = source.getFeaturesAtCoordinate(coordinate);
    if (features.length > 0) {
      map.value.getTargetElement().style.cursor = "pointer";
      let name = features[0].get("name");
      let center = features[0].get("center")
      overlay.setPosition(center)
      //设置覆盖物对应的element的innerHTML
      let element = overlay.getElement();
      element.innerHTML = `
            <p>当前城市是:${name}</p>
            <p>经度:${center[0]}</p>
            <p>纬度:${center[1]}<p>
        `
    } else {
      map.value.getTargetElement().style.cursor = 'default'
      overlay.setPosition(undefined)  //鼠标移出时,设置为undefined,就不会显示了
    }
  })
}
onMounted(async () => {
  await Initialize()
  await ViewCity()
  map.value.on('moveend', () => {
    const currentZoom = map.value.getView().getZoom()
    if (currentZoom > 13) {
      isTagSelectDisabled.value = false;
    } else {
      isTagSelectDisabled.value = true;
    }
  });
  Addoverlay()
})
</script>

<style>
@import 'https://at.alicdn.com/t/c/font_5053822_fjoula92t4s.css';

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

body {
  display: block !important;
  width: 100%;
  height: 100%;
}

.top-area {
  height: 80px;
  background-color: #4264fb;
  position: fixed;
  width: 100%;
  display: flex;
  align-items: center;
  gap: 100px;
}

.city-choose {
  margin-inline: 50px;
  color: #fff;
}

.search-wrapper {
  position: relative;
}

.el-input__wrapper {
  padding-left: 36px !important;
}

.icon-sousuo {
  position: absolute;
  left: 6%;
  top: 50%;
  color: #666666;
  transform: translateY(-50%);
  z-index: 10;
}

#map-container {
  position: absolute;
  top: 80px;
  width: 100%;
  height: calc(100% - 80px);
}

.ol-zoom {
  top: .5em;
  left: .5em;
}

.ol-control {
  position: absolute;
  background-color: rgba(128, 128, 128, 0.25);
  border-radius: 4px;
}

.ol-zoom .ol-zoom-in {
  border-radius: 2px 2px 0 0;
}

.ol-zoom .ol-zoom-out {
  border-radius: 0 0 2px 2px;
}

.ol-control button {
  display: block;
  margin: 1px;
  padding: 0;
  color: #666666;
  font-weight: 700;
  text-decoration: none;
  font-size: inherit;
  text-align: center;
  height: 1.375em;
  width: 1.375em;
  line-height: .4em;
  background-color: white;
  border: none;
}

.ol-control button:hover,
.ol-control button:focus {
  text-decoration: none;
  outline: 1px solid #666666;
  color: #333333;
}

#popup {
  width: 200px;
  height: max-content;
  padding: 10px;
  border-radius: 4px;
  box-shadow: 0 2px 4px #0000004d;
  background-color: pink;
}

#overlay {
  width: 200px;
  height: max-content;
  padding: 10px;
  border-radius: 4px;
  box-shadow: 0 2px 4px #0000004d;
  background-color: pink;
}
</style>
